# -*- coding: utf-8 -*-
# @Time : 2022-01-06 11:29
# @Author : Z
# @Function :
import base64
import re
import time
import traceback
from time import sleep

import ddddocr
import pywinauto
from pywinauto import keyboard
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.select import Select
from Common.Logger import path, logger
from Common.mysqltest import MysqlTest
from Common.ver.slide import Slide
from Common.ver.verify import Verify
from Keywords.mydec import relations


class Web:

    # 初始化
    def __init__(self):
        # 运行自动化的驱动对象,冒号是指定类型,不用加(),可以点出方法,指定类型，可以点出该类型所拥有的方法
        self.driver: webdriver.Chrome
        self.relations = {"a":"123"}
        self.verify = Verify('zengwenxiang', '7812977zwx', '927466')
        self.base64 = ''
        self.verify_len = True
        self.win1 = pywinauto.Desktop()

    # 打开浏览器
    def open_browser(self, browser: str = 'gc'):
        try:
            if browser == 'gc' or browser == '':
                # 在打开浏览器之前，去掉自动化标识
                # option = Options()
                # option.add_experimental_option('excludeSwitches', ['enable-automation'])
                # option.add_argument('--disable-blink-features=AutomationControlled')
                # # 关掉密码弹窗
                # prefs = {}
                # prefs['credentials_enable_service'] = False
                # prefs['profile.password_manager_enabled'] = False
                # option.add_experimental_option('prefs', prefs)
                # self.driver = webdriver.Chrome(chrome_options=option)
                self.driver = webdriver.Chrome()
            elif browser == 'ff':
                self.driver = webdriver.Firefox()
            elif browser == 'ie':
                self.driver = webdriver.Ie()
            elif browser == 'edg':
                self.driver = webdriver.Edge(r'C:\Users\15678\Desktop\python\msedgedriver.exe')
            else:
                self.driver = webdriver.Chrome()
            self.driver.maximize_window()
            self.driver.implicitly_wait(10)
            logger.info("启动浏览器")
            return "启动浏览器"
        except Exception as e:
            logger.error(traceback.format_exc())
            return traceback.format_exc()

    # 打开url页面
    def get_url(self, url: str = ''):
        try:
            self.driver.get(url)
            logger.info("打开页面" + url)
            return "打开页面" + url
        except Exception as e:
            logger.error("打开页面失败" + url)
            return traceback.format_exc()

    # 隐式等待
    def implicitly_wait(self, t: str = ''):
        self.driver.implicitly_wait(float(t))

    # 关联relatisons 字典
    def __get_relations(self, params: str = ''):
        if params:
            for key in self.relations:
                # params.replace(旧值，新值)   新值替换params的旧值，若旧值不存在params中，则保持原来数据
                params = params.replace('{' + key + '}', str(self.relations.get(key)))
        return params

    # 定位元素
    def __find_ele(self, locator: str = ''):
        try:
            if locator is None or locator == '':
                return None
            # xpath定位
            if locator.startswith('//') or locator.startswith('(//'):
                return self.driver.find_element(By.XPATH, locator)
            # css定位
            elif locator.startswith('#') or locator.__contains__('>'):
                return self.driver.find_element(By.CSS_SELECTOR, locator)
            else:
                # 默认id定位
                return self.driver.find_element(By.ID, locator)
        except:
            return traceback.format_exc()

    # 获取网页title
    def get_title(self):
        try:
            title = self.driver.title
            self.relations['title'] = title
            logger.info("当前网页title:"+title)
            return title
        except:
            logger.error("获取页面title失败")
            return traceback.format_exc()

    # 获取元素属性
    def get_attribute(self, locator: str = '', attribute: str = '', key: str = ''):
        locator = self.__get_relations(locator)
        try:
            ele = self.__find_ele(locator)
            actual = ele.get_attribute(attribute)
            self.relations[key] = actual
            logger.info(locator + " " + attribute + " " + str(actual))
            return self.relations
        except:
            logger.error(traceback.format_exc())
            return traceback.format_exc()

    # 输入文本或者上传图片
    def input(self, locator: str = '', value: str = ''):
        locator = self.__get_relations(locator)
        value = self.__get_relations(value)
        try:
            ele = self.__find_ele(locator)
            if value.endswith('.png'):
                value = path + '/lib/imgs/' + value
            ele.clear()
            ele.send_keys(value)
            logger.info(locator + "输入" + value)
            return locator + "输入" + value
        except:
            res = self.__find_ele(locator)
            logger.error("输入失败，请检查" + locator+traceback.format_exc())
            return res

    # pywinauto模拟操作键盘,输上传图片路径
    def upload_image(self,image:str=''):
        try:
            # 选择文件上传窗口，窗口句柄默认打开
            bow1 = self.win1['打开']
            # 选择文件地址输入框，点击激活
            bow1['Toolbar3'].click()
            # 键盘输入文件路径
            keyboard.send_keys(path + r'\lib\imgs')
            # 键盘输入回车，打开该路径
            keyboard.send_keys("{VK_RETURN}")
            # 选中文件名输入框，输入文件名
            bow1['文件名(&N):Edit'].type_keys(image)
            # 键盘输入回车，打开该路径
            keyboard.send_keys("{VK_RETURN}")
            # # 点击打开
            # bow1["打开(&O)"].click()
            logger.info("输入图片成功")
            return "输入图片成功"
        except:
            logger.info("输入图片失败")
            return traceback.format_exc()

    # 下拉框选择
    def select(self, locator: str = '', value: str = ''):
        locator = self.__get_relations(locator)
        try:
            ele = self.__find_ele(locator)
            select = Select(ele)
            try:
                int(value)
                select.select_by_value(value)
                logger.info('下拉选择成功' + locator + " " + value)
                return '下拉选择成功' + locator + " " + value
            except:
                select.select_by_visible_text(value)
                logger.info('下拉选择成功' + locator + " " + value)
                return '下拉选择成功' + locator + " " + value
        except Exception as e:
            res = self.__find_ele(locator)
            logger.error('下拉选择失败' + locator + " " + value)
            return res

    # 进入iframe
    def into_iframe(self, locator: str = ''):
        try:
            locator = self.__get_relations(locator)
            ele = self.__find_ele(locator)
            self.driver.switch_to.frame(ele)
            logger.info("进入iframe成功" + locator)
            return '进入iframe成功' + locator
        except:
            logger.error("进入iframe失败" + locator)
            return traceback.format_exc()

    # 退出iframe，到最初页面
    def out_iframe(self):
        try:
            self.driver.switch_to.default_content()
            logger.info("退出iframe到最初页面成功")
            return '退出iframe'
        except:
            logger.error("退出iframe到最初页面失败")
            return traceback.format_exc()

    # 鼠标悬停
    def hover(self, locator: str = ''):
        try:
            locator = self.__get_relations(locator)
            ele = self.__find_ele(locator)
            action = ActionChains(self.driver)
            action.move_to_element(ele).perform()
            logger.info('鼠标悬停' + locator)
            return '鼠标悬停' + locator
        except:
            res = self.__find_ele(locator)
            logger.error('鼠标悬停失败' + locator)
            return res

    # 通过正则获取文本内容
    def get_text(self, locator: str = '', reg: str = '', key: str = ''):
        try:
            ele = self.__find_ele(locator)
            text = ele.text
            if reg:
                text = re.findall(reg, text)[0]
            self.relations[key] = text
            logger.info('正则提取后的文本内容:' + text)
            return '正则提取后的文本内容:' + text
        except:
            res = self.__find_ele(locator)
            logger.error('正则提取文本内容失败' + locator + " " + reg)
            return res

    # 获取文本内容
    def text(self, locator: str = '', key: str = ''):
        try:
            ele = self.__find_ele(locator)
            text = ele.text
            print(text)
            self.relations[key] = text
            logger.info('获取的文本内容:' + text)
            return self.relations
        except:
            res = self.__find_ele(locator)
            logger.error('获取取文本内容失败' + locator)
            return res

    # 保存参数  先提取到文本内容，然后保存一个参数，最后替换
    @relations
    def save_param(self, paramname: str = '', value: str = ''):
        try:
            # value = self.__get_relations(value)
            self.relations[paramname] = value
            logger.info('保存参数：' + paramname + ":" + value)
            return '保存参数：' + paramname + ":" + value
        except:
            logger.error('保存参数失败：' + paramname + ":" + value)
            return traceback.format_exc()

    # 切换窗口
    def switch_win(self, idx: str = ''):
        try:
            handles = self.driver.window_handles
            # print(handles)
            if idx:
                self.driver.switch_to.window(handles[int(idx)])
            else:
                self.driver.close()
                self.driver.switch_to.window(handles[1])
                # print(handles[1])
            logger.info("切换句柄")
            return '切换句柄'
        except:
            logger.info("切换句柄失败")
            return traceback.format_exc()

    # 点击
    def click(self, locator: str = ''):
        try:
            locator = self.__get_relations(locator)
            ele = self.__find_ele(locator)
            ele.click()
            logger.info('点击' + locator)
            return '点击' + locator
        except:
            res = self.__find_ele(locator)
            logger.error('点击失败' + locator+traceback.format_exc())
            return res, traceback.format_exc()

    # js点击
    @relations
    def click_js(self, locator: str = ''):
        try:
            # locator = self.__get_relations(locator)
            ele = self.__find_ele(locator)
            self.driver.execute_script('arguments[0].click()', ele)
            logger.info('js点击' + locator)
            return 'js点击' + locator
        except:
            res = self.__find_ele(locator)
            logger.error('点击失败' + locator+traceback.format_exc())
            return res, traceback.format_exc()

    # js 滚动页面  ll = "window.scrollTo(100,400);"
    def js_moveto(self, x: str = '', y: str = ''):
        try:
            js = "window.scrollTo(" + x + "," + y + ");"
            self.driver.execute_script(js)
            logger.info(js)
            return js
        except:
            js = "window.scrollTo(" + x + "," + y + ");"
            logger.error("js滚动页面失败" + js)
            return traceback.format_exc()

    # js移动页面到指定元素位置
    def js_moveto_ele(self, locator: str = ''):
        try:
            ele = self.__find_ele(locator)
            self.driver.execute_script("arguments[0].scrollIntoView()", ele)
            logger.info("移动到" + locator)
        except:
            res = self.__find_ele(locator)
            logger.error("移动到" + locator + "元素失败")
            return res

    # 用js方法输入日期
    def js_input(self, locator: str = '', attribute: str = '', value: str = ''):
        try:
            # 去掉train_date元素的readonly只读属性
            # js = 'document.getElementById("train_date").removeAttribute("readonly");'
            js = 'document.getElementById("' + locator + '").removeAttribute("' + attribute + '");'
            self.driver.execute_script(js)
            # js_value = 'document.getElementById("train_date").value="2021-11-15"'
            js_value = 'document.getElementById("' + locator + '").value="' + value + '"'
            self.driver.execute_script(str(js_value))
            logger.info(locator + "  " + value)
            return locator + value
        except:
            logger.error("输入日期时间失败" + locator + " " + value)
            return traceback.format_exc()

    # 关闭整个浏览器
    def quit(self):
        try:
            self.driver.quit()
            logger.info('关闭浏览器')
            return '关闭浏览器'
        except:
            logger.error('关闭浏览器失败')
            return traceback.format_exc()

    # 关闭浏览器当前的窗口
    def close(self):
        try:
            self.driver.close()
            logger.info('关闭浏览器')
            return '关闭浏览器'
        except:
            logger.error('关闭浏览器失败')
            return traceback.format_exc()

    # 设置强制等待时间
    def sleep(self, t: str = ''):
        try:
            sleep(float(t))
            logger.info('等待' + t + "s")
            return '等待' + t + "s"
        except:
            logger.error('等待' + t + "s,失败,请检查是否输入的是数字")
            return traceback.format_exc()

    # 获取图形验证码
    def get_verify(self, locator: str = ''):
        try:
            ele = self.__find_ele(locator)
            # 元素截图
            ele.screenshot(path + '/lib/verify/verify.png')
            # 调用验证码的库，识别验证码
            ver = self.verify.PostPic(1005, im=path + '/lib/verify/verify.png')
            self.relations['verify'] = ver
            logger.info('获取' + locator + "元素的验证码" + ver)
            return '获取' + locator + "元素的验证码：" + ver
        except:
            logger.error('获取' + locator + "元素的验证码失败")
            return traceback.format_exc()

    def refresh(self):
        self.sleep("1")
        self.driver.refresh()
        logger.info("刷新页面")

    # 获取闪烁的图形验证码
    def get_verify_flashed(self, locator: str = ''):
        try:
            # 获取验证码元素的src  下载地址
            base64_code = self.__find_ele(locator).get_attribute('src')
            # 截取图片的 base64 编码
            base64_code = base64_code[22:]
            # 下载到图片本地
            f = open(path + '/lib/verify/base64_code.gif', mode='wb')
            f.write(base64.b64decode(base64_code))
            f.close()
            # 调用验证码的库，识别验证码  6001
            ver = self.verify.PostPic(1005, im='../../lib/verify/base64_code.gif')
            self.relations['verify_base64'] = ver
            logger.info('获取' + locator + "元素的验证码" + ver)
            return '获取' + locator + "元素的验证码：" + ver
        except:
            logger.error('获取' + locator + "元素的验证码失败")
            return traceback.format_exc()

    # 判断页面元素内容是否符合预期结果
    def assertEleText(self, locator: str = '', expected: str = ''):
        try:
            ele = self.__find_ele(locator)
            text = ele.text
            if text == expected:
                logger.info("断言成功")
                return "断言成功"
            else:
                error = "断言失败,实际结果和预期结果不一致。" + text + " " + expected
                logger.error(error)
                # 执行报错的函数，在pytest里面将用例置为fail
                res = self.__fail_Traceback()
                return error + "。" + res
        except Exception as e:
            res = self.__find_ele(locator)
            logger.error(res)
            return res

    # 判断元素是否存在页面
    def assertEleExit(self, locator: str = ''):
        res = self.__find_ele(locator)
        logger.info(res)
        return res

    # 查询数据库数据，进行判断
    def assertMysql(self, sql: str = '', param: str = '', expected: str = ''):
        try:
            mytest = MysqlTest()
            result = mytest.mysql_execute(sql)
            actual = result[0].get(param)
            if str(actual) == expected:
                logger.info("查到的结果是" + str(actual) + ",测试成功")
                return "查到的结果是" + str(actual) + ",测试成功"
            else:
                error = "查到的结果是" + str(actual) + ",预期的结果是" + expected + "，测试失败"
                logger.error(error)
                # 执行报错的函数，在pytest里面将用例置为fail
                res = self.__fail_Traceback()
                return error + "。" + res
        except:
            logger.error("数据库查询出错，测试失败" + traceback.format_exc())
            return traceback.format_exc()

    # 格式化当前时间  "%Y-%m-%d %H:%M:%S"
    def now_time(self, format: str = ''):
        now_time = time.strftime(format, time.localtime())
        self.relations['time'] = now_time
        logger.info(now_time)
        return now_time

    # 输入随机数字的文本
    def inputRandom(self, locator: str = '', value: str = '', format: str = ''):
        try:
            value = self.now_time(format) + value
            value = self.__get_relations(value)
            ele = self.__find_ele(locator)
            if value.endswith('.png'):
                value = path + '/lib/imgs/' + value
            ele.send_keys(value)
            logger.info(locator + "输入" + value)
            return locator + "输入" + value
        except:
            res = self.__find_ele(locator)
            logger.error("输入失败，请检查：" + locator)
            return res

    # 报错函数
    def __fail_Traceback(self):
        try:
            a = 1
            b = "str"
            c = a + b
            return c
        except:
            return traceback.format_exc()

    # qq滑块验证码
    def slide(self, locator: str = '', locator1: str = '', locator2: str = ''):
        try:
            slide_obj = Slide(self.driver)
            slide_obj.slide_img(locator, locator1, locator2)
            logger.info("滑块拼图成功")
            return "滑块拼图成功"
        except:
            logger.error("滑块拼图失败")
            return traceback.format_exc()

    # jd滑块验证码 pyautogui
    def slide_jd(self, locator: str = '', locator1: str = '', locator2: str = ''):
        try:
            slide_obj = Slide(self.driver)
            slide_obj.slid_imgjd(locator, locator1, locator2)
            logger.info("滑块拼图完成")
            return 1
        except:
            logger.error("滑块拼图失败")
            return traceback.format_exc()

    #  qq滑块验证码 pyautogui
    def slide_qq(self, locator: str = '', locator1: str = '', locator2: str = ''):
        try:
            slide_obj = Slide(self.driver)
            slide_obj.slid_imgqq(locator, locator1, locator2)
            logger.info("滑块拼图完成")
            return 1
        except:
            logger.error("滑块拼图失败")
            return traceback.format_exc()

    # 获取普通验证码，精度一般
    def free_verify(self, locator: str = ''):
        try:
            ele = self.__find_ele(locator)
            # 元素截图
            ele.screenshot(path + '/lib/verify/verify.png')
            ocr = ddddocr.DdddOcr()
            with open(path + '/lib/verify/verify.png', 'rb') as f:
                aa = f.read()
                f.close()
            res = ocr.classification(aa)
            # 调用验证码的库，识别验证码
            self.relations['verify'] = res
            logger.info('获取' + locator + "元素的验证码" + res)
            return '获取' + locator + "元素的验证码：" + res
        except:
            logger.error('获取' + locator + "元素的验证码失败")
            return traceback.format_exc()

    # 设置窗口大小
    def set_windowsize(self, x: str = '', y: str = ''):
        try:
            x = int(x)
            y = int(y)
            self.driver.set_window_size(x, y)
            logger.info("窗口大小设置为："+str(x)+","+str(y))
            return "窗口大小设置为："+str(x)+","+str(y)
        except:
            logger.error('窗口大小设置失败')
            return traceback.format_exc()
